package com.jiaqiang.chatgpt.domain.security.service;

import com.jiaqiang.chatgpt.domain.security.model.vo.JwtToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定义Filter，用于拦截携带Token的请求
 */
public class JwtFilter extends AccessControlFilter {

    private final Logger logger = LoggerFactory.getLogger(JwtFilter.class);

    /**
     * isAccessAllowed 判断是否携带有效的 JwtToken
     * 所以这里直接返回一个 false，让它走 onAccessDenied 方法流程
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return false;
    }

    /**
     * 返回结果为true表明登录通过
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        // 如果你设定的 token 放到 header 中，则可以这样获取；request.getHeader("Authorization");
        JwtToken jwtToken = new JwtToken(request.getParameter("token")); // 从 query 参数中获取 token
        try {
            // 鉴权认
            // 尝试获取当前用户的 Subject（主体），getSubject 是 AccessControlFilter 类的方法，用于获取当前用户的主体信息
            Subject subject = getSubject(servletRequest, servletResponse);
            // 调用主体的 login 方法，将 JWT token 传入进行登录认证。
            // subject.login(jwtToken)会触发Shiro框架去执行身份认证
            // 而实际的认证逻辑会委托给 JwtRealm 中的doGetAuthenticationInfo方法来处理
            // 为什么是 JwtRealm ? 在 ShiroConfig 中配置了 -> [return new JwtRealm();]
            subject.login(jwtToken);
            // 如果认证成功，返回 true, 表示用户可以访问受保护资源。
            return true;
        } catch (Exception e) {
            // 如果认证失败, 则捕获异常, 记录错误日志
            logger.error("鉴权认证失败", e);
            // 并调用 onLoginFail 方法进行失败处理
            onLoginFail(servletResponse);
            // 返回 false, 表示用户无法访问受保护资源
            return false;
        }
    }

    /**
     * 鉴权认证失败时默认返回 401 状态码
     */
    private void onLoginFail(ServletResponse response) throws IOException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        httpResponse.getWriter().write("Auth Error!");
    }

}
